package game.driver;

import java.awt.Color;
import java.util.List;

import game.gui.Board;
import game.gui.ColorSelectGUI;
import game.gui.NewGameGUI;
import game.gui.PlayerWonGUI;
import game.model.Card;
import game.model.Deck;
import game.player.*;
import game.utils.Util;

@SuppressWarnings("unused")
public class Controller {

	/**
	 * @param args
	 */
	private Player[] players;
	private Board b;
	private Deck deckUp;
	private Deck deckDown;
	private Util util;
	private boolean isDirClockwise;
	private int currentPlayer, previousPlayer;
	private int nextPlayer;
	private GameLogic gameLogic;
	private boolean gameOn = false; 
	private int theHuman = 0;
	
	/**
	 * main method which runs the "startBoard" method.
	 * @param String[]
	 */
	public static void main(String args[]) {
		Controller c = new Controller();
		c.startBoard();
	}
	
	/**
	 * 
	 * Controller constructor. Forms new GameLogic().
	 */
	public Controller() {
		gameLogic = new GameLogic();
	}

	/**
	 * starts the GUI Board.
	 * 
	 */
	private void startBoard() {
		b = new Board(this);
	}

	/**
	 * Starts a new game and all of the pieces that go along with it.
	 * @param String[] p, contains the players
	 * @param int[] isHuman, contains -1 for non humans players or an int value for the level of difficulty of a computer player.
	 */
	public void newGame(String[] p, int[] isHuman) {
		isDirClockwise = true;
		players = new Player[p.length];
		createPlayers(p, isHuman);
		deckDown = new Deck(false);
		deckUp = new Deck(true);
		dealCards();
		deckUp.push(deckDown.pop());
		currentPlayer = (int) (Math.random() * p.length);
		if(currentPlayer == players.length - 1) {
			nextPlayer = 0;
		}
		else {
			nextPlayer = currentPlayer + 1;
		}
		if(currentPlayer == 0) {
			previousPlayer = players.length - 1;
		}
		else {
			previousPlayer = currentPlayer - 1;
		}
		gameOn = true;
		b.game();
		for(int x = 0; x < 10; x++) {
			play();
		}
	}
	
	/**
	 * 
	 * @return boolean, Returns a true if a game has been started, false if a game has yet to begin.
	 */
	public boolean gameOn() {
		return gameOn;
	}

	/**
	 * 
	 * Save the game.
	 */
	public void saveGame() {
		util.saveGame(players, deckUp, deckDown);
	}

	/**
	 * 
	 * @param String, location which to save the game.
	 */
	public void saveGame(String s) {
		util.saveGame(s, players, deckUp, deckDown);
	}

	/**
	 * 
	 * @param String, location where the file is saved and to be loaded from.
	 */
	public void loadGame(String s) {
		List<Object> load = util.loadGame(s);
		if ((Boolean) load.get(0)) {
			gameOn = true;
			String[] names = (String[]) (load.get(1));
			int[] isHuman = (int[]) (load.get(2));
			players = new Player[names.length];
			createPlayers(names, isHuman);
		}
	}

	private void dealCards() {
		for (int x = 0; x < 7; x++) {
			for (int y = 0; y < players.length; y++) {
				Card c = deckDown.pop();
				players[y].drawCards(c);
			}
		}
	}

	private boolean createPlayers(String[] p, int[] isHuman) {
		if (p.length != isHuman.length || p.length < 2 || p.length > 4)
			return false;
		else {
			int x = 0;
			while (x < p.length) {
				if (isHuman[x] == -1) {
					players[x] = new HumanPlayer(this, p[x], -1);
				} else {
					players[x] = new AIPlayer(this, p[x], isHuman[x]);
				}
				x++;
			}
			return true;
		}
	}

	/**
	 * @param int, the index of the player
	 * @return Player, that Player
	 */
	public Player getPlayer(int x) {
		return players[x];
	}

	/**
	 * Returns the current player
	 * @return Player
	 */
	public Player getCurrentPlayer() {
		return players[currentPlayer];
	}

	/**
	 * Returns the next player
	 * @return Player
	 */
	public Player getNextPlayer() {
		return players[nextPlayer];
	}
	
	/**
	 * Returns the previous player
	 * @return Player
	 */
	public Player getPreviousPlayer() {
		return players[previousPlayer];
	}

	/**
	 * Returns the direction the game is going
	 * @return boolean
	 */
	public boolean isClockwise() {
		return isDirClockwise;
	}

	private void reverse() {
		if (isDirClockwise) {
			if (currentPlayer == 0) {
				nextPlayer = players.length - 1;
			} else {
				nextPlayer = currentPlayer - 1;
			}
		} else if (!isDirClockwise) {
			if (currentPlayer == players.length - 1) {
				nextPlayer = 0;
			} else {
				nextPlayer = currentPlayer + 1;
			}
		}

		isDirClockwise = !isDirClockwise;
	}

	private void skip() {
		if (isDirClockwise) {
			if (nextPlayer == players.length - 1) {
				nextPlayer = 0;
			} else {
				nextPlayer++;
			}
		} else {
			if (nextPlayer == players.length - 1) {
				nextPlayer = 0;
			} else {
				nextPlayer++;
			}
		}
	}

	private void drawTwo() {
		for (int x = 0; x < 2; x++) {
			if (deckDown.peek() != null) {
				getNextPlayer().drawCards(deckDown.pop());
			} else {
				Card c = deckDown.refillDeck(deckDown);
				deckUp.clear();
				deckUp.push(c);
				getNextPlayer().drawCards(deckDown.pop());
			}
		}
		skip();
	}

	private void wild() {
		if (getCurrentPlayer().isHuman()) {
			ColorSelectGUI stuff = new ColorSelectGUI(this);
		} else {
			AIPlayer temp = (AIPlayer) getCurrentPlayer();
			temp.selectWildColor();
		}
	}

	private void wildDrawFour() {
		for (int x = 0; x < 4; x++) {
			if (deckDown.peek() != null) {
				getNextPlayer().drawCards(deckDown.pop());
			} else {
				Card c = deckDown.refillDeck(deckDown);
				deckUp.clear();
				deckUp.push(c);
				getNextPlayer().drawCards(deckDown.pop());
			}
		}
		if (getCurrentPlayer().isHuman()) {
			System.out.println("Stuff!");
			ColorSelectGUI stuff = new ColorSelectGUI(this);
		} else {
			AIPlayer temp = (AIPlayer) getCurrentPlayer();
			temp.selectWildColor();
		}
		skip();
	}
	
	/**
	 * Returns the card atop the discard (faceup) deck.
	 * @return Card
	 */
	public Card seeTopCard() {
		return deckUp.peek();
	}

	
	/**
	 * Returns number of players in the game.
	 * @return int
	 */
	public int numberOfPlayer() {
		return players.length;
	}

	
	/**
	 * Returns draws a card for the current player.
	 * 
	 */
	public void drawCard() {
		if (deckDown.peek() != null) {
			getCurrentPlayer().drawCards(deckDown.pop());
		} else {
			Card c = deckDown.refillDeck(deckDown);
			deckUp.clear();
			deckUp.push(c);
			getCurrentPlayer().drawCards(deckDown.pop());
		}
	}
	
	private void playTurn() {
		
		Player cp = getCurrentPlayer();
		Card ca = (Card) deckUp.peek();
		Card c = cp.play(ca);
		int count = 0;
		while(c == null) {
			count++;
			System.out.println(count);
			drawCard();
			c = cp.play(ca);
		}
		playCard(c);
	}
	
	private void play() {
		while(currentPlayer != theHuman) {
			playTurn();
		}
		b.startTurn();
	}
	
	public void finishTurn(Card c) {
		playCard(c);
	}

	/**
	 * Draws a card for the current player. Called by either 
	 * 
	 */
	public void playCard(Card c) {
		Card temp = c;
		
		if (gameLogic.playableCard(temp, deckUp.peek()) && getCurrentPlayer().hasCard(temp)) {
			getCurrentPlayer().getCard(temp);
			if (getCurrentPlayer().hasNumCards() == 1) {
				unoTime();
			}
			deckUp.push((Card) temp);
			if (temp == null) {
				drawCard();
			} else if (gameLogic.isReverse(temp))
				reverse();
			else if (gameLogic.isSkip(temp)) {
				skip();
			} else if (gameLogic.isDrawTwo(temp)) {
				drawTwo();
			} else if (gameLogic.isWild(temp)) {
				wild();
			} else if (gameLogic.isWildDrawFour(temp)) {
				wildDrawFour();
			}
		}
		turnComplete();
	}
	
	private int unoTime() {
		long shortestTime = -1;
		int shortestTimePlayer = -1;
		for (int x = 0; x < players.length; x++) {
			if (players[x].isHuman()) {
				long temp = b.unoTime();
				if (shortestTime == -1) {
					shortestTime = temp;
					shortestTimePlayer = x;
				} else if (shortestTime > temp) {
					shortestTime = temp;
					shortestTimePlayer = x;
				}
			} else {
				AIPlayer tempPlayer = (AIPlayer) getCurrentPlayer();
				long temp = tempPlayer.unoTime()*1000000000;
				if (shortestTime == -1) {
					shortestTime = temp;
				} else if (shortestTime > temp) {
					shortestTime = temp;
					shortestTimePlayer = x;
				}
			}
		}
		return shortestTimePlayer;
	}

	private void turnComplete() {
		System.out.println(getCurrentPlayer().getName() + " is ending their turn.");
		if(gameLogic.hasPlayerWon(getCurrentPlayer())) {
			PlayerWonGUI stuff = new PlayerWonGUI(getCurrentPlayer());
		}
		else {
			previousPlayer = currentPlayer;
			currentPlayer = nextPlayer;
			if(isDirClockwise) {
				if(currentPlayer == players.length - 1) {
					nextPlayer = 0;
				}
				else {
					nextPlayer = currentPlayer + 1;
				}
			}
			else {
				if(currentPlayer == 0) {
					nextPlayer = players.length - 1;
				}
				else {
					nextPlayer = currentPlayer - 1;
				}
			}
		}
		System.out.println("Now it is " + getCurrentPlayer().getName() + "'s turn.");
		b.refresh();
		System.out.println("Refreshed.");
	}
	
	/**
	 * Not finished, but important class. Well, that sucks.
	 * @param Color, the color make the wild card.
	 */
	public void setWildColor(Color c) {
		gameLogic.setWildColor(c);
	}

}